var ns = ns || {};

/**
 * Constructor
 * @returns {ns.Connection}
 */
ns.Connection = function() {
	this.conn = new WebSocket("ws://localhost:8080/ws");
	this.conn.binaryType = "arraybuffer";
	this.conn.onopen = function(evt) {
		console.log('Connection opened.');
	};
	this.conn.onclose = function(evt) {
		console.log('Connection closed.');
	};
	this.conn.onmessage = function(e) {
		console.log(e.data);
		if (e.data instanceof ArrayBuffer) {
			s = "ArrayBuffer: " + e.data.byteLength + " [";
			var view = new Uint8Array(e.data);
			for ( var i = 0; i < view.length; ++i) {
				s += " " + view[i];
			}
			s += "]";
			console.log(s);
		}
	};
};

/**
 * Tests the websockets support
 * @param text
 */
ns.Connection.prototype.send = function(text) {
	this.conn.send(text);
};
/**
 * Class to manage WebGL methods.
 */

/**
 * Constructor
 * @param canvasId
 * @param vertexShaderId
 * @param fragmentShaderId
 */
ns.WebGLmanager = function(canvasId, vertexShaderId, fragmentShaderId) {
	this.canvasId = canvasId;
	this.vertexShaderId = vertexShaderId;
	this.fragmentShaderId = fragmentShaderId;
	this.gl = null;
	this.shaderProgram = null;
};

/**
 * Initializes the WebGL context.
 */
ns.WebGLmanager.prototype._initContext = function() {
	var canvas = document.getElementById(this.canvasId);
	try {
		this.gl = canvas.getContext("experimental-webgl");
		if(this.gl == null){
			throw "Could not initialise WebGL";
		}
	} catch (e) {
		throw "[ERROR] Could not initialise WebGL.";
	}
};

/**
 * Retrieves a shader
 * @param shaderId
 * @returns
 */
ns.WebGLmanager.prototype._getShader = function(shaderId) {
	var shaderScript = document.getElementById(shaderId);
	if (!shaderScript) {
		throw "[ERROR] No shader script in element " + shaderId;
	}

	var shaderSource = "";
	var currentChild = shaderScript.firstChild;

	while (currentChild) {
		if (currentChild.nodeType == currentChild.TEXT_NODE) {
			shaderSource += currentChild.textContent;
		}

		currentChild = currentChild.nextSibling;
	}

	var shader;
	if (shaderScript.type == "x-shader/x-fragment") {
		shader = this.gl.createShader(this.gl.FRAGMENT_SHADER);
	} else if (shaderScript.type == "x-shader/x-vertex") {
		shader = this.gl.createShader(this.gl.VERTEX_SHADER);
	} else {
		throw "[ERROR] Undefined shader type.";
	}

	this.gl.shaderSource(shader, shaderSource);
	this.gl.compileShader(shader);

	if (!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
		throw "[ERROR] Shader compilation error. "
				+ this.gl.getShaderInfoLog(shader);
	}

	return shader;
};

/**
 * Initializes the WebGL shaders
 */
ns.WebGLmanager.prototype._initShaders = function() {
	var fragmentShader = this._getShader(this.fragmentShaderId);
	var vertexShader = this._getShader(this.vertexShaderId);

	this.shaderProgram = this.gl.createProgram();
	this.gl.attachShader(this.shaderProgram, vertexShader);
	this.gl.attachShader(this.shaderProgram, fragmentShader);
	this.gl.linkProgram(this.shaderProgram);

	if (!this.gl.getProgramParameter(this.shaderProgram, this.gl.LINK_STATUS)) {
		throw "[ERROR] Could not initialise shaders";
	}

	this.gl.useProgram(this.shaderProgram);
};

/**
 * Initializes WebGL
 */
ns.WebGLmanager.prototype.init = function() {
	this._initContext();
	this._initShaders();
};

/**
 * Retrieves the WebGL context
 * @returns
 */
ns.WebGLmanager.prototype.getContext = function() {
	return this.gl;
};

/**
 * Retrieves the shader program
 * @returns
 */
ns.WebGLmanager.prototype.getShaderProgram = function() {
	return this.shaderProgram;
};var socket;

function send() {
	socket.send('Hello world');
}

function main() {
	try {
		socket = new ns.Connection();

		var webGLmanager = new ns.WebGLmanager("canvas", "shader-vs",
				"shader-fs");
		webGLmanager.init();
		var gl = webGLmanager.getContext();
		var shaderProgram = webGLmanager.getShaderProgram();

		var canvas = document.getElementById("canvas");
		var width = canvas.width;
		var height = canvas.height;

		var shaderManager = new ns.ShaderManager(gl, shaderProgram, width,
				height);
		shaderManager.init();

		var vertices = [0.0, 1.0, 0.0,
		                -1.0, -1.0, 0.0,
		                1.0, -1.0, 0.0];
		shaderManager.initBuffer(vertices);
		
		shaderManager.drawScene();

	} catch (e) {
		console.error(e);
	}
}
/**
 * Class to configure the shader program
 */

/**
 * Constructor
 * @param gl
 * @param shaderProgram
 * @param width
 * @param height
 */
ns.ShaderManager = function(gl, shaderProgram, width, height){
	this.gl = gl;
	this.shaderProgram = shaderProgram;
	this.width = width;
	this.height = height;
	
	this.vertexPositionAttribute = null;
	this.pMatrixUniform = null;
	this.mvMatrixUniform = null;
	
	this.mvMatrix = mat4.create();
	this.pMatrix = mat4.create();
	
	this.triangleVertexPositionBuffer = null;
};

/**
 * Initializes the openGL attributes
 */
ns.ShaderManager.prototype._initGLattributes = function(){
	this.gl.clearColor(0.0, 0.0, 0.0, 1.0);
    this.gl.enable(this.gl.DEPTH_TEST);
};

/**
 * Initializes the attributes' locations in the shader program
 */
ns.ShaderManager.prototype._initAttributeLocation = function(){
	this.vertexPositionAttribute = 
		this.gl.getAttribLocation(this.shaderProgram, "aVertexPosition");
	this.pMatrixUniform =
		this.gl.getUniformLocation(this.shaderProgram, "uPMatrix");
	this.mvMatrixUniform =
		this.gl.getUniformLocation(this.shaderProgram, "uMVMatrix");
};

/**
 * Initializes the shader program
 */
ns.ShaderManager.prototype.init = function(){
	this._initGLattributes();
    
	this._initAttributeLocation();
	
	this.gl.enableVertexAttribArray(this.vertexPositionAttribute);
};

/**
 * Initializes the buffer with the provided vertices
 * @param vertices
 */
ns.ShaderManager.prototype.initBuffer = function(vertices){
	this.triangleVertexPositionBuffer = this.gl.createBuffer();
	this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.triangleVertexPositionBuffer);
    
	this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(vertices),
			this.gl.STATIC_DRAW);
	this.triangleVertexPositionBuffer.itemSize = 3;
	this.triangleVertexPositionBuffer.numItems = 3;
};

/**
 * Draws the scene
 */
ns.ShaderManager.prototype.drawScene = function(){
	this.gl.viewport(0, 0, this.width, this.height);
	this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

    mat4.perspective(45, this.width / this.height, 0.1, 100.0, this.pMatrix);

    mat4.identity(this.mvMatrix);

    mat4.translate(this.mvMatrix, [-1.5, 0.0, -7.0]);
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.triangleVertexPositionBuffer);
    this.gl.vertexAttribPointer(this.vertexPositionAttribute,
    		this.triangleVertexPositionBuffer.itemSize, this.gl.FLOAT,
    		false, 0, 0);
    
    this.gl.uniformMatrix4fv(this.pMatrixUniform, false, this.pMatrix);
	this.gl.uniformMatrix4fv(this.mvMatrixUniform, false, this.mvMatrix);
    
	this.gl.drawArrays(this.gl.LINE_STRIP, 0, this.triangleVertexPositionBuffer.numItems);
};